package com.project.springboot.aspect;

import com.project.springboot.annotation.LogCostTime;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * description: 自定义方法耗时AOP切面
 * @author 朱正磊
 * @date 2025-10-14 13:23:15
 */
@Slf4j
@Aspect
@Component
public class CostTimeAspect {

    /**
     * description: 环绕通知
     * @author 朱正磊
     * @date 2025-10-14 13:31:44
     * @param joinPoint 可执行连接点
     * @param logCostTime 方法执行耗时注解
     * @return 返回目标方法返回值
     * @throws Throwable 异常信息
     */
    @Around("@annotation(logCostTime)")
    public Object around(ProceedingJoinPoint joinPoint, LogCostTime logCostTime) throws Throwable {
        // 目标方法名
        String methodName = joinPoint.getSignature().getName();
        // 目标方法描述
        String methodDesc = logCostTime.value();
        // 目标方法执行耗时预警(ms)
        long consumeWarn = logCostTime.consumeWarn();

        // 开始时间(ns)
        long start = System.nanoTime();
        Object result;

        try {
            result = joinPoint.proceed();
        } finally {
            // 目标方法执行耗时(ns)
            long costNanos = System.nanoTime() - start;
            // 目标方法执行耗时(ms)
            long costMillis = TimeUnit.NANOSECONDS.toMillis(costNanos);
            // 根据耗时预警值决定日志输出级别
            if (consumeWarn > 0L && costMillis > consumeWarn) {
                // 如果目标方法执行耗时预警(ms)大于0并且阈值小于目标方法实际耗时，警告输出日志
                log.warn("方法：{}.{}({}) 实际耗时：{}ms(阈值：{}ms)", joinPoint.getTarget().getClass().getSimpleName(), methodName, methodDesc, costMillis, consumeWarn);
            } else {
                // 如果目标方法执行耗时预警(ms)使用的默认值0L或者阈值不小于目标方法实际耗时，正常输出日志
                log.info("方法：{}.{}({}) 实际耗时：{}ms", joinPoint.getTarget().getClass().getSimpleName(), methodName, methodDesc, costMillis);
            }
        }

        return result;
    }

}
